﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Prsrp
{
    public delegate void StateCallback();

    public class State
    {
        // Protected Properties
        protected StateCallback mpPreCallback;
        protected StateCallback mpMainCallback;
        protected StateCallback mpPostCallback;
        // Public Properties
        public void ExecutePreCallback()  { if (mpPreCallback  != null) mpPreCallback();  }
        public void ExecuteMainCallback() { if (mpMainCallback != null) mpMainCallback(); }
        public void ExecutePostCallback() { if (mpPostCallback != null) mpPostCallback(); }
        // Constructor
        public State(Action _pMainAction, Action _pPreAction, Action _pPostAction)
        {
            if (_pPreAction  != null) mpPreCallback = new StateCallback(_pPreAction);
            if (_pMainAction != null) mpMainCallback = new StateCallback(_pMainAction);
            if (_pPostAction != null) mpPostCallback = new StateCallback(_pPostAction);
        }
    }

    /// <summary>
    /// This class define a managable element.
    /// </summary>
    /// <remarks>
    /// Each class who herit from BaseStateMachine must override the following functions:
    /// . LoadStates()
    /// . Update()
    /// </remarks>
    public class BaseStateMachine : BaseObject
    {
        #region Properties
        public enum BaseStates
        {
            kInit,
            kIdle,
            kEnd
        }
        
        // State Container
        public int miCurState;
        public int miOldState;

        // States Delegate Functions
        //public delegate void StateCallback();

        public List<State> mtabStateCallbacks = new List<State>();

        #endregion

        #region State Callbacks
        
        public virtual void OnInit() { throw new NotImplementedException(); }
        public virtual void OnIdle() { throw new NotImplementedException(); }

        #endregion

        #region Methods & Functions

        /// <summary>
        /// Add new <typeparamref name="_pStateFunction"/> delegate to <typeparamref name="mtabStateCallbacks"/>.
        /// </summary>
        /// <remarks>
        /// Parameter <typeparamref name="_StateIndex"/> must be castable as an integer.
        /// This method is used to prevent abusing casting of enum value to integer.
        /// </remarks>
        /// <param name="_StateIndex">The index in <typeparamref name="mtabStateCallbacks"/> to add the delegate.</param>
        /// <param name="_pStateFunction">The delegate function to add to <typeparamref name="mtabStateCallbacks"/>.</param>
        /// <param name="_pPreStateFunction">The delegate functoin add to <typeparamref name="mtabStateCallbacks"/> as pre callback.</param>
        /// <param name="_pPostStateFunction">The delegate functoin add to <typeparamref name="mtabStateCallbacks"/> as post callback.</param>
        /// <example>
        /// Define an idle state. (assuming the OnIdle() is declared)
        /// <code>
        /// DefineState(BaseStates.kIdle, OnIdle);
        /// </code>
        /// </example>
        protected void DefineState(object _StateIndex, Action _pStateFunction, Action _pPreStateFunction, Action _pPostStateFunction)
        {
            int iStateIndex = (int)_StateIndex;

            // Resize List if needed.
            while (iStateIndex > mtabStateCallbacks.Count) mtabStateCallbacks.Add(null);

            // Insert Content
            mtabStateCallbacks.Insert(iStateIndex, new State(_pStateFunction, _pPreStateFunction, _pPostStateFunction));
        }

        /// <summary>
        /// Shortcut to DefineState()
        /// </summary>
        protected void DefineState(object _StateIndex, Action _pStateFunction)
        {
            DefineState(_StateIndex, _pStateFunction, null, null);
        }

        /// <summary>
        /// Set the current state and update background flags.
        /// </summary>
        /// <remarks>
        /// Parameter <paramref name="_NewState"/> must be castable as an integer.
        /// This method is used to prevent abusing casting of enum value to integer.
        /// </remarks>
        /// <param name="_NewState"></param>
        public void SetState(object _NewState)
        {
            mtabStateCallbacks[miCurState].ExecutePostCallback();
            int iNewState = (int)_NewState;
            miOldState = miCurState;
            miCurState = iNewState;
            mtabStateCallbacks[miCurState].ExecutePreCallback();
        }

        /// <summary>
        /// This method execute the delegate associated with current state.
        /// </summary>
        /// <exception cref="IndexOutOfRangeException">
        /// Thrown when there is no delegate associate with the current state.
        /// </exception>
        protected void ExecuteStateFunction()
        {
            mtabStateCallbacks[miCurState].ExecuteMainCallback();
        }

        #endregion

        #region Abstract Functions

        /// <summary>
        /// This function need to be updated for eachs new state enum.
        /// </summary>
        public virtual void LoadStates()
        {
            DefineState(BaseStates.kInit, OnInit);
            DefineState(BaseStates.kIdle, OnIdle);
        }

        /// <summary>
        /// This function will be called at each update cycle. 
        /// Use it to make state-machine work.
        /// </summary>
        public virtual void Update() { throw new NotImplementedException(); }

        #endregion
    }
}
